iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
Modern Web

了不起的 Svelte系列 第 19

第 19 天:Svelte 中的邏輯運作:`if` 邏輯區塊

  • 分享至 

  • xImage
  •  

第 19 天:Svelte 中的邏輯運作:if 邏輯區塊

第 19 天要講的是

  1. 介紹 Svelte 的 if 邏輯區塊語法
  2. 在專案中使用 if 邏輯區塊

  經過了三天的消化,我們知道了該如何運用 Svelte 提供的 each 邏輯區塊,讓我們可以運用類似 Javascript forEach 的邏輯運算,快速地根據 Javascript 陣列變數做出重複的 HTML 元素。是不是非常的方便又實用呢?今天就讓我們再接再厲,繼續學另一種 Svelte 所提供的邏輯區塊吧。

1. 介紹 Svelte 的 if 邏輯區塊語法

  一般的 HTML 並沒辦法提供條件式的運算來決定哪些 HTML 元素需要呈現給使用者,又是哪些 HTML 元素需要移除掉。但是為了能夠做出互動式 (reactive) 的使用者介面,這看起來是個不可或缺的功能。作為一個受到眾多工程師們喜愛的前端框架,Svelte 理所當然的能夠實現這個願望。程式碼的寫法如下:

{#if expression}
  <!-- 將需要的 HTML 元素放在這裡 -->
{/if}
  • 第一行:{#if expression}...{/if}
      用 {#if} 開啟邏輯段落。expression 則是 Javascript 的表達式,能夠算出一個 true 或是 false 的結果。

  • 第二行:<!-- 將需要的 HTML 元素放在這裡 -->
      如果前面 expression 的值,經過布林值的轉換,結果為 true,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。反之,如果為 false,就不會出現這些 HTML 元素。

  • 第三行:{/if}
      記得要用 {/if} 結束這個邏輯段落。

  既然有 if 的邏輯段落,那想必會有跟 else 邏輯段落的組合囉:

{#if expression}
  <!-- 如果 expression 為 true,則顯示這邊的 HTML 元素 -->
{:else}
  <!-- 否則顯示這邊的 HTML 元素 -->
{/if}
  • 第一行:{#if expression}
      用 {#if} 開啟邏輯段落。接著不贅述。

  • 第二行:<!-- 如果 expression 為 true,則顯示這邊的 HTML 元素 -->
      如果前面 expression 的值,經過布林值的轉換,結果為 true,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。

  • 第三行:{:else}
      用 {:else}if 這個邏輯段落做展延。

  • 第四行:<!-- 否則顯示這邊的 HTML 元素 -->
      如果前面 expression 的值,經過布林值的轉換,結果為 false,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。

  • 第五行:{/if}
      最後,還是記得要用 {/if} 結束這個邏輯段落。

  既然有 ifelse 的邏輯段落,那想必又會再有 else if 邏輯段落的組合囉:

{#if expression1}
  <!-- 如果 expression1 為 true,則顯示這邊的 HTML 元素 -->
{:else if expression2}
  <!-- 如果 expression2 為 true,則顯示這邊的 HTML 元素 -->
{/if}
  • 第一行:{#if expression}
      用 {#if} 開啟邏輯段落。接著不贅述。

  • 第二行:<!-- 如果 expression1 為 true,則顯示這邊的 HTML 元素 -->
      如果 expression1 的值,經過布林值的轉換,結果為 true,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。

  • 第三行:{:else if expression2}
      用 {:else}if 這個邏輯段落做展延,並繼續做第二個條件 expression2 的計算。

  • 第四行:<!-- 否則顯示這邊的 HTML 元素 -->
      如果 expression2 的值,經過布林值的轉換,結果為 true,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。

  • 第五行:{/if}
      最後,還是記得要用 {/if} 結束這個邏輯段落。

  既然有 ifelseelse if 的邏輯段落,那想必又會再有沒完沒了的各種組合囉?沒錯,Svelte 就是提供了一個這麼富有彈性又方便的 if 邏輯段落。

在專案中使用 if 邏輯區塊

  學到一個這麼好用的工具,不實際來運用看看豈不可惜。就讓我們試試看用 if 邏輯區塊去動態顯示色票的鎖定跟解除鎖定的狀態吧。直接來到 Palettes.svelte 的程式碼:

/src/lib/Palettes.svelte
<script>
  import unlock from "../assets/unlock.svg";
  import lock from "../assets/lock.svg";
  export let palettes;
</script>

<div class="palettes">
  {#each palettes as { hex, id, locked } (id)}
    <div class="card">
      <div class="palette" style="background: #{hex}" />
      <div class="hex-code">
        <p>
          {hex}
        </p>
      </div>
      <!-- svelte-ignore a11y-no-static-element-interactions a11y-click-events-have-key-events -->
      <div class="lock-icon">
        {#if locked}
          <img src={lock} alt="color-locked" />
        {:else}
          <img src={unlock} alt="color-unlocked" />
        {/if}
      </div>
    </div>
  {:else}
    <div class="card no-color">
      <div>
        <p>No color to show. Please add a color.</p>
      </div>
    </div>
  {/each}
</div>
  • 第三行:import lock from "../assets/lock.svg";
      為了要表達鎖定色票的狀態,我們同樣也從 Font Awesome 參考了上鎖的圖示。詳細的程式碼可以參考 Font Awesome 的網站(這裡)。

  • 第八行:{#each palettes as { hex, id, locked } (id)}
      因為我們需要從 palettes 每個物件當中的 locked 這個鍵值 (key) 來判斷該色票是否為鎖定的狀態,所以記得用解構賦值把 locked 也拿出來。

  • 第十八行:{#if locked}
      用 {#if} 開啟 if 的邏輯段落。並且用 locked 這個變數的布林值來做為顯示 HTML 元素的依據。

  • 第十九行:<img src={lock} alt="color-locked" />
      如果 locked 這個布林值為 true,則顯示上鎖這個圖示。

  • 第二十行:{:else}
      用 {:else} 展延出 else 的邏輯段落。

  • 第二十一行:<img src={unlock} alt="color-unlocked" />
      如果 locked 的布林值為 false,則顯示解鎖這個圖示。

  • 第二十二行:{/if}
      記得用 {/if} 來結束整個 if 邏輯段落。

  雖然把 if 邏輯段落做好了,但是仔細檢視看看我們的專案,應該就會注意到現在所有色票的 locked 都為 false,這樣就看不出來 if 邏輯段落是否符合我們所設計的方式去運作。讓我們把新增的色票的 locked 都改成 true 試試看吧:

/src/App.svelte
<!-- 在 Javascript 當中 import Counter -->
<script>
  import Counter from "./lib/Counter.svelte";
  import Modal from "./lib/Modal.svelte";
  import Palettes from "./lib/Palettes.svelte";

  let showModal = false;

  let palettes = [
    { hex: "ff4000", locked: false, id: "init_01" },
    { hex: "32e6e3", locked: false, id: "init_02" },
    { hex: "009fe9", locked: false, id: "init_03" },
  ];

  $: count = palettes.length;

  const generateTone = () =>
    ("0" + Math.round(255 * Math.random()).toString(16)).slice(-2);
  const generateHex = () =>
    [generateTone(), generateTone(), generateTone()].reduce(
      (a, c) => a + c,
      ""
    );
  const generatePalette = (() => {
    let uuid = 0;
    return () => ({
      hex: generateHex(),
      locked: true,
      id: `${uuid++}`,
    });
  })();

  let someState = "TheGreatSvelte";
  const sparkle = (text) => {
    const sparkles = ["★", "☆", "✧", "✪"];
    const randomSparkles = () =>
      sparkles[Math.floor(Math.random() * sparkles.length)];
    const sparkledText = text
      .split("")
      .reduce((a, c) => a + randomSparkles() + c, "");
    return sparkledText;
  };

  const href = "https://ithelp.ithome.com.tw/users/20120178/ironman/7031";

  const handleClick = (e) => {
    console.log(e);
    const tobeCount = count + e.detail;
    if (0 <= tobeCount && tobeCount < 7) {
      switch (e.detail) {
        case 1:
          palettes = [...palettes, generatePalette()];
          break;
        case -1:
          palettes = palettes.slice(0, -1);
          break;
      }
    } else showModal = true;
  };
</script>

  這麼一大段程式碼,有改變的只有這一行:

  • 第二十八行:locked: true,
      讓新增的色票 locked 變成 true 吧。

https://i.ibb.co/wWxZCh0/19.gif
圖一,新增的色票都給他鎖起來

  太棒了,看來我們做出來的 if 邏輯段落是真的有發揮作用的。那麼野心勃勃的人,是不是想要更進一步去實作互動的功能,讓使用者可以藉由點擊這個上鎖/解鎖圖示,來改變上鎖/解鎖的狀態呢?沒問題,讓我們藉由第十三天學過的客製化事件來完成這個任務吧:

/src/lib/Palettes
<script>
  import { createEventDispatcher } from "svelte";
  import unlock from "../assets/unlock.svg";
  import lock from "../assets/lock.svg";
  export let palettes;

  const dispatch = createEventDispatcher();
  const handleClick = (id) => dispatch("changeLock", id);
</script>

<div class="palettes">
  {#each palettes as { hex, id, locked } (id)}
    <div class="card">
      <div class="palette" style="background: #{hex}" />
      <div class="hex-code">
        <p>
          {hex}
        </p>
      </div>
      <!-- svelte-ignore a11y-no-static-element-interactions a11y-click-events-have-key-events -->
      <div class="lock-icon" on:click={() => handleClick(id)}>
        {#if locked}
          <img src={lock} alt="color-locked" />
        {:else}
          <img src={unlock} alt="color-unlocked" />
        {/if}
      </div>
    </div>
  {:else}
    <div class="card no-color">
      <div>
        <p>No color to show. Please add a color.</p>
      </div>
    </div>
  {/each}
</div>
  • 第二行:import { createEventDispatcher } from "svelte";
      引入客製化事件需要的 createEventDispatcher

  • 第七行:const dispatch = createEventDispatcher();
      初始化客製化事件產生器。

  • 第八行:const handleClick = (id) => dispatch("changeLock", id);
      客製化一個 changeLock 事件,隨著該事件傳送出 id 的資訊,讓我們主要元件 (App.svelte) 能夠知道應該改變哪一個色票的 locked 狀態。

  • 第二十一行:<div class="lock-icon" on:click={() => handleClick(id)}>
      在這個 <div> 上加上我們的事件處理器 handleClick,只要一點擊,就發送 changeLock 事件。

  接著來到我們的 App.svelte,需要做些修改,來接收子元件 Palettes.svelte 發送過來的 changeLock 事件:

/src/App.svelte
<!-- 在 Javascript 當中 import Counter、Modal、Palettes -->
<script>
  import Counter from "./lib/Counter.svelte";
  import Modal from "./lib/Modal.svelte";
  import Palettes from "./lib/Palettes.svelte";

  let showModal = false;

  let palettes = [
    { hex: "ff4000", locked: false, id: "init_01" },
    { hex: "32e6e3", locked: false, id: "init_02" },
    { hex: "009fe9", locked: false, id: "init_03" },
  ];

  $: count = palettes.length;

  const generateTone = () =>
    ("0" + Math.round(255 * Math.random()).toString(16)).slice(-2);
  const generateHex = () =>
    [generateTone(), generateTone(), generateTone()].reduce(
      (a, c) => a + c,
      ""
    );
  const generatePalette = (() => {
    let uuid = 0;
    return () => ({
      hex: generateHex(),
      locked: true,
      id: `${uuid++}`,
    });
  })();

  let someState = "TheGreatSvelte";
  const sparkle = (text) => {
    const sparkles = ["★", "☆", "✧", "✪"];
    const randomSparkles = () =>
      sparkles[Math.floor(Math.random() * sparkles.length)];
    const sparkledText = text
      .split("")
      .reduce((a, c) => a + randomSparkles() + c, "");
    return sparkledText;
  };

  const href = "https://ithelp.ithome.com.tw/users/20120178/ironman/7031";

  const handleClick = (e) => {
    console.log(e);
    const tobeCount = count + e.detail;
    if (0 <= tobeCount && tobeCount < 7) {
      switch (e.detail) {
        case 1:
          palettes = [...palettes, generatePalette()];
          break;
        case -1:
          palettes = palettes.slice(0, -1);
          break;
      }
    } else showModal = true;
  };

  const handleLock = (e) => {
    console.log(e);
    const targetId = e.detail;
    palettes = palettes.map((palette) => {
      if (palette.id === targetId) {
        return { ...palette, locked: !palette.locked };
      } else {
        return palette;
      }
    });
  };
</script>
  • 第六十一行:const handleLock = (e) => {
      宣告一個函式 handleLock,用來處理 changeLock 事件。這個函式需要用我們的客製化事件當作參數,所以在參數的部分寫一個 e 代表。

  • 第六十二行:console.log(e);
      將事件 e 記錄下來。單純方便偵錯使用,不一定需要這一行。

  • 第六十三行:const targetId = e.detail;
      我們伴隨客製化事件 changeLock 一起送出來的資料會放在哪邊呢?就是在 e.detail 當中,所以用 targetId 將目標的 id 儲存起來。接下來的程式碼就不細講了。大意就是在 palettes 當中找出 id === targetId 的色票,並切換該色票的 locked 布林值。原本為 true 的就變成 false,原本為 false 的就變成 true

https://i.ibb.co/vmhkfLK/19.gif
圖二、三個可以自由解鎖/開鎖的色票

  好的,那麼今天關於 if 邏輯段落的介紹就到這邊了,謝謝大家。


上一篇
第 18 天:Svelte 中的邏輯運作:`each` 邏輯區塊(三)
下一篇
第 20 天:Svelte 中的邏輯運作:`await` 邏輯區塊
系列文
了不起的 Svelte30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言